/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package engine.entities.inventory.item;

import engine.entities.inventory.Inventory;
import engine.entities.inventory.item.InventoryItemRenderer.ItemRendererListener;
import java.awt.Component;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.IOException;
import javax.swing.BorderFactory;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.TransferHandler;
import javax.swing.TransferHandler.TransferSupport;

/**
 *
 * @author Nathan T
 */
public abstract class InventoryTable extends JPanel {

    public static final int ITEM_RENDER_SCALE = 2;
    public static final DataFlavor ITEM_DATA_FLAVOR = getItemDataFlavor();

    protected final int rows;
    protected final int columns;
    protected final InventoryItemRenderer[][] items;
    protected Inventory inventory;
    protected InventoryTableListener listener;

    public InventoryTable(Inventory inventory) {
        this.rows = inventory.getRowCount();
        this.columns = inventory.getColumnCount();
        this.inventory = inventory;
        this.listener = null;
        this.items = new InventoryItemRenderer[rows][columns];
        this.init();
    }
    
    public InventoryTable(int rows, int columns) {
        this.rows = rows;
        this.columns = columns;
        this.inventory = null;
        this.items = new InventoryItemRenderer[rows][columns];
        this.init();
    }
    
    private void init() {
        this.setBorder(BorderFactory.createRaisedBevelBorder());
        
        initRenderers();

        buildTable();
    }
    
    abstract protected void initRenderers();

    private void buildTable() {
        this.setLayout(new GridBagLayout());
        GridBagConstraints c;

        for (int i = 0; i < items.length; i++) {
            for (int j = 0; j < items[0].length; j++) {
                items[i][j].setItemRendererListener(new TableItemListener(i, j));
                c = new GridBagConstraints();
                c.gridx = j;
                c.gridy = i;
                c.weightx = 0.0;
                c.weighty = 0.0;
                c.fill = GridBagConstraints.NONE;
                this.add(items[i][j], c);
            }
        }
    }

    public void setInventoryTableListener(InventoryTableListener listener) {
        this.listener = listener;
    }
    
    public static final DataFlavor getItemDataFlavor() {
        try {
            return new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType + Item.class.getName());
        }
        catch (ClassNotFoundException ex) {
            ex.printStackTrace();
        }
        return null;
    }

    public static class ItemTransferHandler extends TransferHandler {

        @Override
        public int getSourceActions(JComponent comp) {
            return MOVE;
        }

        @Override
        public Transferable createTransferable(JComponent comp) {
            if (!(comp instanceof InventoryItemRenderer)) {
                return null;
            }
            return new ItemTransferable(new ItemTransferPacket(((InventoryItemRenderer) comp).getInvItem(), (InventoryItemRenderer) comp));
        }

        @Override
        public void exportDone(JComponent comp, Transferable trans, int action) {

        }

        @Override
        public boolean canImport(TransferSupport support) {
            try {
                if (!support.isDataFlavorSupported(ITEM_DATA_FLAVOR)) {
                    return false;
                }
                Component comp = support.getComponent();
                if (!(comp instanceof InventoryItemRenderer)) {
                    return false;
                }

                InventoryItemRenderer rend = (InventoryItemRenderer) comp;

                Object trans = support.getTransferable().getTransferData(ITEM_DATA_FLAVOR);
                if (!(trans instanceof ItemTransferPacket)) {
                    System.out.println("1");
                    return false;
                }
                InventoryItem item = ((ItemTransferPacket) trans).getInvItem();
                return item.getItem().canEquipTo(rend.getSlot());
            }
            catch (UnsupportedFlavorException | IOException ex) {
                ex.printStackTrace();
            }
            return false;
        }

        @Override
        public boolean importData(TransferSupport support) {
            Component comp = support.getComponent();
            if (!(comp instanceof InventoryItemRenderer)) {
                return false;
            }
            InventoryItemRenderer targetRenderer = (InventoryItemRenderer) comp;

            Transferable trans = support.getTransferable();
            try {
                Object o = trans.getTransferData(ITEM_DATA_FLAVOR);
                if (!(o instanceof ItemTransferPacket)) {
                    return false;
                }
                ItemTransferPacket packet = (ItemTransferPacket) o;
                InventoryItemRenderer originRenderer = packet.getRenderer();
                InventoryItem transferedItem = packet.getInvItem();
                InventoryItem targetItem = targetRenderer.getInvItem();

                if (originRenderer.equals(targetRenderer)) {
                    // We are not moving anything, so do nothing
                    // But we are done with the transfer, so return complete.
                    return true;
                }

                boolean transferComplete = false;
                if (targetItem != null && transferedItem != null) {
                    if (targetItem.getItem().canStackWith(transferedItem.getItem())) {
                        targetItem.setQuantity(targetItem.getQuantity() + transferedItem.getQuantity());
                        originRenderer.setInvItem(null);
                        transferComplete = true;
                    }
                }
                if (!transferComplete) {
                    originRenderer.setInvItem(targetItem);
                    targetRenderer.setInvItem(transferedItem);
                    transferComplete = true;
                }

                return transferComplete;
            }
            catch (UnsupportedFlavorException | IOException ex) {
                ex.printStackTrace();
            }
            return false;
        }

    }

    public static class ItemTransferable implements Transferable {

        private ItemTransferPacket packet;

        public ItemTransferable(ItemTransferPacket packet) {
            this.packet = packet;
        }

        @Override
        public DataFlavor[] getTransferDataFlavors() {
            DataFlavor[] flavors = new DataFlavor[1];
            flavors[0] = ITEM_DATA_FLAVOR;
            return flavors;
        }

        @Override
        public boolean isDataFlavorSupported(DataFlavor flavor) {
            return flavor.equals(ITEM_DATA_FLAVOR);
        }

        @Override
        public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
            if (!isDataFlavorSupported(flavor)) {
                throw new UnsupportedFlavorException(flavor);
            }
            return packet;
        }

    }

    private static class ItemTransferPacket {

        private final InventoryItem item;
        private final InventoryItemRenderer renderer;

        public ItemTransferPacket(InventoryItem item, InventoryItemRenderer renderer) {
            this.item = item;
            this.renderer = renderer;
        }

        public InventoryItem getInvItem() {
            return item;
        }

        public InventoryItemRenderer getRenderer() {
            return renderer;
        }
    }

    public static interface InventoryTableListener {
        void itemChanged(int row, int column, InventoryItem newItem);
    }
    
    private class TableItemListener implements ItemRendererListener {

        private int row;
        private int column;
        
        public TableItemListener(int row, int column) {
            this.row = row;
            this.column = column;
        }
        
        @Override
        public void itemChanged(InventoryItem item) {
            if (listener != null) {
                listener.itemChanged(row, column, item);
            }
        }
        
    }
    
}
